//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjNinjaSync.h"
#include "NjDummySyncObject.h"

//----------------------------------------------------------------------------
#if NJ_DUMMY_SYNC_OBJECT_TRACKING_ENABLED
#	include "NjDebugLocklessCircularBuffer.h"

	class NjDummySyncObject::TrackerEntry
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(TrackerEntry);
		NJ_TRIVIAL_DESTRUCTOR(TrackerEntry);

		NjDummySyncObject const * m_pDummySyncObject;
		NjUint m_uThreadDebugId;
		NjUint m_uLine;

		static const NjUint BUFFER_SIZE = 1024;

		NJ_DISABLE_COPY(TrackerEntry);
	};
	//----------------------------------------------------------------------------
	class NjDummySyncObject::Tracker
	{
	public:
		static void Record(NjDummySyncObject const * pDummySyncObject, NjUint uThreadDebugId, NjUint uLine);

	private:
		typedef NjDebugLocklessCircularBuffer<NjDummySyncObject::TrackerEntry, NjDummySyncObject::TrackerEntry::BUFFER_SIZE> CircularBuffer;
		static CircularBuffer ms_kCircularBuffer;

		NJ_DISABLE_INSTANCE(Tracker);
	};

#	define NJ_DUMMY_SYNC_OBJECT_TRACKING() Tracker::Record(this, uThreadDebugId, __LINE__)
#else // NJ_DUMMY_SYNC_OBJECT_TRACKING_ENABLED
#	define NJ_DUMMY_SYNC_OBJECT_TRACKING()
#endif // NJ_DUMMY_SYNC_OBJECT_TRACKING_ENABLED
//----------------------------------------------------------------------------
#if NJ_DUMMY_SYNC_OBJECT_TRACKING_ENABLED || NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
	void NjDummySyncObject::DebugInitialize()
	{
#	if NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
		m_kThreadDebugId.Set(NjEventsSystem::INVALID_THREAD_ID);
		m_uDebugCounter=0;
#	endif // NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
	}
	//----------------------------------------------------------------------------
	void NjDummySyncObject::DebugUninitialize()
	{
#	if NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
		NJ_ASSERT(m_uDebugCounter==0 && m_kThreadDebugId.Get()==NjEventsSystem::INVALID_THREAD_ID);
#	endif // NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
	}
	//----------------------------------------------------------------------------
	void NjDummySyncObject::DebugLock()
	{
		NjUint uThreadDebugId;
		if (!NjEventsSystem::GetCurrentThreadDebugId(uThreadDebugId))
		{
			NJ_BREAK();
			uThreadDebugId=NjEventsSystem::INVALID_THREAD_ID;
		}

		NJ_DUMMY_SYNC_OBJECT_TRACKING();

#	if NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
		NjUint const uPreviousThreadDebugId(m_kThreadDebugId.PostExchange(uThreadDebugId));

		if (uPreviousThreadDebugId==NjEventsSystem::INVALID_THREAD_ID)
			return;

		NJ_ASSERT(uPreviousThreadDebugId==uThreadDebugId);
		++m_uDebugCounter;
#	endif // NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
	}
	//----------------------------------------------------------------------------
	void NjDummySyncObject::DebugUnlock()
	{
		NjUint uThreadDebugId;
		if (!NjEventsSystem::GetCurrentThreadDebugId(uThreadDebugId))
		{
			NJ_BREAK();
			uThreadDebugId=NjEventsSystem::INVALID_THREAD_ID;
		}

		NJ_DUMMY_SYNC_OBJECT_TRACKING();
		
#	if NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
		NJ_ASSERT(m_kThreadDebugId.Get()==uThreadDebugId);

		if (m_uDebugCounter!=0)
			--m_uDebugCounter;
		else
			m_kThreadDebugId.Set(NjEventsSystem::INVALID_THREAD_ID);
#	endif // NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
	}
#endif // NJ_DUMMY_SYNC_OBJECT_TRACKING_ENABLED || NJ_DUMMY_SYNC_OBJECT_STORE_LOCKER_ENABLED
//----------------------------------------------------------------------------
#if NJ_DUMMY_SYNC_OBJECT_TRACKING_ENABLED
	void NjDummySyncObject::Tracker::Record(NjDummySyncObject const * pDummySyncObject, NjUint uThreadDebugId, NjUint uLine)
	{
		TrackerEntry * const pEntry(ms_kCircularBuffer.Record());

		pEntry->m_pDummySyncObject=pDummySyncObject;
		pEntry->m_uThreadDebugId=uThreadDebugId;
		pEntry->m_uLine=uLine;
	}
	//----------------------------------------------------------------------------
	NjDummySyncObject::Tracker::CircularBuffer NjDummySyncObject::Tracker::ms_kCircularBuffer;
#endif // NJ_DUMMY_SYNC_OBJECT_TRACKING_ENABLED
//----------------------------------------------------------------------------
